// WMP.h
#include "List.h"
#include "RWLocker.h"

#pragma once

typedef BOOL (WINAPI *PGetModuleHandleExA)(DWORD dwFlags, LPCSTR lpModuleName, HMODULE* phModule);
typedef BOOL (WINAPI *PGetModuleHandleExW)(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE* phModule);
typedef void (WINAPI *PSetPrevAddress)(LPCSTR method, void* proc);
typedef HRESULT (WINAPI *PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv);

extern bool Initialized;
extern bool Uninitialized;

typedef List<HMODULE, PSetPrevAddress> RestoreModuleList;
extern RestoreModuleList* RestoreModules;

typedef List<LPCSTR, PSetPrevAddress> RestoreProcList;
extern RestoreProcList* RestoreProcs;

volatile extern bool HookUninitializing;
extern CriticalSectionLocker* CSLocker;

void Uninitialize();
void Initialize(bool nolock = false);

HRESULT WINAPI StartThePlayerProxy(void*, INT_PTR, int);
HRESULT WINAPI CheckThePlayerProxy(LPSTR*, PDWORD, PDWORD, PDWORD, PDWORD);
void WINAPI ReleaseDataObjProxy(LPCWSTR);
HRESULT WINAPI UpdateSharedFoldersProxy();
HRESULT CallFunctionElevatedProxy(LPCWSTR);
bool WINAPI ShouldActiveSetupBeRunProxy();
HRESULT WINAPI DllCanUnloadNow();
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv);
HRESULT WINAPI DllRegisterServer();
HRESULT WINAPI DllUnregisterServer();
HRESULT WINAPI InitializeTests();
HRESULT WINAPI CheckThePlayer11Proxy(INT_PTR, INT_PTR, INT_PTR, INT_PTR, INT_PTR);

void HookHandle(HMODULE* hModule);
void WMPHookFinalization();

void AcquireLock();
void ReleaseLock();

#define SyncProcHook(Type, Select, Args) \
void* proc = Select; \
if (!HookUninitializing) { \
  (*(Type)proc)Args; \
  return; \
} \
AcquireLock(); \
try { \
  (*(Type)Select)Args; \
} catch ( ... ) { \
  ReleaseLock(); \
  throw; \
} \
ReleaseLock();

#define SyncFuncHook(Result, Type, Select, Args) \
void* proc = Select; \
if (!HookUninitializing) { \
  return (*(Type)proc)Args; \
} \
Result res; \
AcquireLock(); \
try { \
  res = (*(Type)Select)Args; \
} catch ( ... ) { \
  ReleaseLock(); \
  throw; \
} \
ReleaseLock(); \
return res;
